using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Middleware
{
    /// <summary>
    /// 请求响应日志记录中间件
    /// </summary>
    public class RequestResponseLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<RequestResponseLoggingMiddleware> _logger;

        public RequestResponseLoggingMiddleware(RequestDelegate next, ILogger<RequestResponseLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var stopwatch = Stopwatch.StartNew();
            var requestId = Guid.NewGuid().ToString();
            
            // 添加请求ID到响应头
            context.Response.Headers["X-Request-ID"] = requestId;
            
            // 记录请求信息
            await LogRequestAsync(context, requestId);

            // 保存原始响应流
            var originalResponseBodyStream = context.Response.Body;

            try
            {
                using var responseBodyStream = new MemoryStream();
                context.Response.Body = responseBodyStream;

                await _next(context);

                stopwatch.Stop();

                // 记录响应信息
                await LogResponseAsync(context, requestId, stopwatch.ElapsedMilliseconds, responseBodyStream);

                // 将响应写回原始流
                responseBodyStream.Seek(0, SeekOrigin.Begin);
                await responseBodyStream.CopyToAsync(originalResponseBodyStream);
            }
            finally
            {
                context.Response.Body = originalResponseBodyStream;
            }
        }

        private async Task LogRequestAsync(HttpContext context, string requestId)
        {
            var request = context.Request;
            
            var logInfo = new
            {
                RequestId = requestId,
                Method = request.Method,
                Path = request.Path,
                QueryString = request.QueryString.ToString(),
                Headers = GetHeaders(request.Headers),
                UserAgent = request.Headers["User-Agent"].ToString(),
                RemoteIpAddress = context.Connection.RemoteIpAddress?.ToString(),
                UserId = context.User?.Identity?.Name,
                Timestamp = DateTime.UtcNow
            };

            _logger.LogInformation("HTTP Request: {RequestInfo}", System.Text.Json.JsonSerializer.Serialize(logInfo));

            // 记录请求体（仅对POST/PUT请求且非文件上传）
            if (ShouldLogRequestBody(request))
            {
                var requestBody = await ReadRequestBodyAsync(request);
                if (!string.IsNullOrWhiteSpace(requestBody))
                {
                    _logger.LogDebug("Request Body for {RequestId}: {RequestBody}", requestId, requestBody);
                }
            }
        }

        private async Task LogResponseAsync(HttpContext context, string requestId, long elapsedMilliseconds, MemoryStream responseBodyStream)
        {
            var response = context.Response;
            
            var responseBody = string.Empty;
            if (ShouldLogResponseBody(response))
            {
                responseBodyStream.Seek(0, SeekOrigin.Begin);
                responseBody = await new StreamReader(responseBodyStream).ReadToEndAsync();
                responseBodyStream.Seek(0, SeekOrigin.Begin);
            }

            var logInfo = new
            {
                RequestId = requestId,
                StatusCode = response.StatusCode,
                ElapsedMilliseconds = elapsedMilliseconds,
                Headers = GetHeaders(response.Headers),
                ContentType = response.ContentType,
                ContentLength = response.ContentLength,
                Timestamp = DateTime.UtcNow
            };

            var logLevel = GetLogLevel(response.StatusCode);
            _logger.Log(logLevel, "HTTP Response: {ResponseInfo}", System.Text.Json.JsonSerializer.Serialize(logInfo));

            if (!string.IsNullOrWhiteSpace(responseBody))
            {
                _logger.LogDebug("Response Body for {RequestId}: {ResponseBody}", requestId, responseBody);
            }
        }

        private async Task<string> ReadRequestBodyAsync(HttpRequest request)
        {
            request.EnableBuffering();
            
            using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);
            var body = await reader.ReadToEndAsync();
            request.Body.Position = 0;
            
            return body;
        }

        private static bool ShouldLogRequestBody(HttpRequest request)
        {
            // 只记录POST/PUT请求的body，且不是文件上传
            return (request.Method == "POST" || request.Method == "PUT") &&
                   request.ContentType != null &&
                   !request.ContentType.Contains("multipart/form-data") &&
                   request.ContentLength < 10240; // 小于10KB
        }

        private static bool ShouldLogResponseBody(HttpResponse response)
        {
            // 只记录JSON响应且小于10KB
            return response.ContentType != null &&
                   response.ContentType.Contains("application/json") &&
                   (response.ContentLength == null || response.ContentLength < 10240);
        }

        private static LogLevel GetLogLevel(int statusCode)
        {
            return statusCode switch
            {
                >= 500 => LogLevel.Error,
                >= 400 => LogLevel.Warning,
                _ => LogLevel.Information
            };
        }

        private static object GetHeaders(IHeaderDictionary headers)
        {
            var headerDict = new Dictionary<string, string>();
            
            foreach (var header in headers)
            {
                // 排除敏感信息
                if (IsSensitiveHeader(header.Key))
                {
                    headerDict[header.Key] = "***";
                }
                else
                {
                    headerDict[header.Key] = header.Value.ToString();
                }
            }
            
            return headerDict;
        }

        private static bool IsSensitiveHeader(string headerName)
        {
            var sensitiveHeaders = new[]
            {
                "authorization",
                "cookie",
                "x-api-key",
                "x-auth-token"
            };

            return sensitiveHeaders.Contains(headerName.ToLowerInvariant());
        }
    }
}
